////////////////////////////////////////////////////////////////////// 
// libsrc/StringBasics.cpp 
// (c) 2000-2008 Goncalo Abecasis
// 
// This file is distributed as part of the MaCH source code package   
// and may not be redistributed in any form, without prior written    
// permission from the author. Permission is granted for you to       
// modify this file for your own personal use, but modified versions  
// must retain this copyright notice and must not be distributed.     
// 
// Permission is granted for you to use this file to compile MaCH.    
// 
// All computer programs have bugs. Use this file at your own risk.   
// 
// Saturday April 12, 2008
// 
 
#include "StringBasics.h"
#include "Error.h"
#include "Constant.h"
#include "MathConstant.h"

#include <string.h>
#include <ctype.h>
#include <stdarg.h>

#define SWP(A,B) {int tmp=a; a=b; b=tmp;}

#ifdef  _MSC_VER
#ifndef snprintf
#define vsnprintf _vsnprintf
#define snprintf  _snprintf
#endif
#endif

// If natural ordering is defined, comparisons will
// order strings including numbers correctly
// (eg, ... "8", "9", "10" ...) rather than using
// ASCII ordering (... "10", "8", "9", ...)
#define NATURAL_ORDERING         1

int  String::alloc = 8;
bool String::caseSensitive = true;

void String::NewString(int startsize)
   {
   len = 0;
   size = (startsize + alloc) / alloc * alloc;
   buffer = new char [size];
   buffer[0] = 0;
   }

String::String(const char * s)
   {
   int clen = s == NULL ? 0 : strlen(s);
   NewString(clen);
   if (clen)
      {
      len  = clen;
      memcpy(buffer, s, len + 1);
      }
   }

String::String(char ch, int count)
   {
   NewString(count);
   memset(buffer, ch, count);
   buffer[count] = 0;
   len = count;
   }

String::String(const String & s)
   {
   len = s.len;
   size = (s.len + alloc) / alloc * alloc;;
   buffer = new char [size];
   memcpy(buffer, s.buffer, len + 1);
   }

void String::Grow(int newSize)
   {
   if (newSize >= size)
      {
      if ((newSize >> 1) >= size)
         size = (newSize + alloc) / alloc * alloc;
      else
         {
         size = alloc;
         while (size <= newSize)
            size *= 2;
         }

      char * tmp = new char [size];
      memcpy(tmp, buffer, len + 1);
      delete [] buffer;
      buffer = tmp;
      }
   }

void String::Swap(String & s)
   {
   char * temp = s.buffer;
   s.buffer = buffer;
   buffer = temp;

   int swap = s.size;
   s.size = size;
   size = swap;

   swap = s.len;
   s.len = len;
   len = swap;
   }

String & String::Copy(const String & s)
   {
   Grow(s.len);
   len = s.len;
   memcpy(buffer, s.buffer, len + 1);
   return *this;
   }

String & String::Copy(const String & s, int start, int n)
   {
   if (s.len <= start) return Clear();
   if (s.len < start + n) n = s.len - start;
   Grow(n);
   memcpy (buffer, s.buffer + start, n);
   buffer[len = n] = 0;
   return *this;
   }

String & String::Copy(const char * s)
   {
   if (s == NULL)
      {
      len = 0;
      buffer[0] = 0;
      }
   else
      {
      int clen = strlen(s);
      Grow(clen);
      len = clen;
      memcpy(buffer, s, len + 1);
      }
   return *this;
   }

String & String::ToUpper()
   {
   for (int i = 0; i < len; i++)
      buffer[i] = (char) toupper(buffer[i]);
   return *this;
   }

String & String::ToLower()
   {
   for (int i = 0; i < len; i++)
      buffer[i] = (char) tolower(buffer[i]);
   return *this;
   }

String String::AsUpper()
   {
   String temp;
   temp = *this;
   return temp.ToUpper();
   }

String String::AsLower()
   {
   String temp;
   temp = *this;   
   return temp.ToLower();
   }

String String::Capitalize()
   {
   String temp;
   temp = *this;   
   temp.buffer[0] = (char) toupper(temp.buffer[0]);
   return temp;
   }

String & String::operator = (const String & rhs)
   {
   Copy(rhs);
   return *this;
   }

String & String::operator = (const char * rhs)
   {
   Copy(rhs);
   return * this;
   }

String & String::operator += (const String & rhs)
   {
   Grow(len + rhs.len);
   memcpy(buffer + len, rhs.buffer, rhs.len + 1);
   len += rhs.len;
   return *this;
   }

String & String::operator += (const char * rhs)
   {
   if (rhs != NULL)
      {
      int clen = strlen(rhs);
      Grow(len + clen);
      memcpy(buffer + len, rhs, clen + 1);
      len += clen;
      }
   return *this;
   }

String String::operator + (const String & rhs) const
   {
   String result(len + rhs.len);
   memcpy(result.buffer, buffer, len);
   memcpy(result.buffer + len, rhs.buffer, rhs.len + 1);
   result.len = len + rhs.len;
   return result;
   }

String String::operator + (const char * rhs) const
   {
   if (rhs != NULL)
      {
      int clen = strlen(rhs);
      String result(len + clen);
      memcpy(result.buffer, buffer, len);
      memcpy(result.buffer + len, rhs, clen + 1);
      result.len = len + clen;
      return result;
      }
   return *this;
   }

String & String::operator = (char ch)
   {
   if (ch)
      {
      Grow(1);
      buffer[0] = ch;
      buffer[1] = 0;
      len = 1;
      }
   else
      len  = buffer[0] = 0;
   return *this;
   }

String & String::operator += (char ch)
   {
   if (ch)
      {
      Grow(len + 1);
      buffer[len] = ch;
      buffer[++len] = 0;
      }
   return *this;
   }

String String::operator + (char ch) const
   {
   String result(*this);
   result += ch;
   return result;
   }

String & String::operator = (int rhs)
   {
   Clear();

   if (rhs < 0)
      {
      Add('-');
      *this += (unsigned int) -rhs;
      }
   else
      *this = (unsigned int) rhs;
   return *this;
   }

String & String::operator = (unsigned int rhs)
   {
   Clear();

   unsigned long  base = 10;
   int   digits = 1;

   while (rhs >= base)
      {
      base *= 10;
      digits++;
      }

   Grow(digits);

   while (base /= 10)
      {
      char ch = char (rhs / base);
      rhs = rhs - ch * base;
      buffer[len++] = char (ch + '0');
      }
   buffer[len] = 0;
   return *this;
   };

String String::operator + (int rhs) const
   {
   String result(*this);
   result += rhs;
   return result;
   };

String String::operator + (unsigned int rhs) const
   {
   String result(*this);
   result += rhs;
   return result;
   };

String & String::operator += (int rhs)
   {
   String temp;
   temp = rhs;
   return *this += temp;
   }

String & String::operator += (unsigned int rhs)
   {
   String temp;
   temp = rhs;
   return *this += temp;
   }

String & String::operator *= (unsigned int rhs)
   {
   if (rhs == 0)
      Clear();
   else
      {
      String original(*this);

      Grow(len * rhs);

      for (unsigned int i = 1; i < rhs; i++)
         *this += original;
      }
   return *this;
   }

String & String::operator = (double rhs)
   {
   LockBuffer(32);
   sprintf(buffer, "%.3f", rhs);
   UnlockBuffer();
   return *this;
   }

String String::operator + (double rhs) const
   {
   String result(*this);
   result += rhs;
   return result;
   }

String & String::operator += (double rhs)
   {
   String temp;
   temp = rhs;
   return *this += temp;
   }

char * String::LockBuffer(int min)
   {
   if (min > 0) Grow(min);
   return buffer;
   }

String & String::UnlockBuffer()
   {
   for (len = 0; len < size; len++)
      if (buffer[len] == 0)
         return *this;
   error("BasicString - direct access overflowed buffer");
   return *this;
   }

int String::Compare(const String & s) const
   {
   if (caseSensitive)
      return String::FastCompare(s);
   else
      return String::SlowCompare(s);
   }

int String::Compare(const char * s) const
   {
   return caseSensitive ? FastCompare(s) : SlowCompare(s);
   }

int String::FastCompare(const String & s) const
   {
   for (int i = 0; i <= len; i++)
      if (buffer[i] - s.buffer[i])
         {
#ifdef NATURAL_ORDERING
         int d = i;
         while (isdigit(buffer[d]) && isdigit(s.buffer[d]))
            d++;
         if (isdigit(buffer[d]))
            return 1;
         if (isdigit(s.buffer[d]))
            return -1;
#endif
         return buffer[i] - s.buffer[i];
         }
   return 0;
   }

int String::FastCompare(const char * s) const
   {
   if (s == NULL)
      return -len;

   for (int i = 0; i <= len; i++)
      if (buffer[i] - s[i])
         {
#ifdef NATURAL_ORDERING
         int d = i;
         while (isdigit(buffer[d]) && isdigit(s[d]))
            d++;
         if (isdigit(buffer[d]))
            return 1;
         if (isdigit(s[d]))
            return -1;
#endif
         return buffer[i] - s[i];
         }
   return 0;
   }

int String::SlowCompare(const String & s) const
   {
   for (int i = 0; i <= len; i++)
      if (toupper(buffer[i]) - toupper(s.buffer[i]))
         {
#ifdef NATURAL_ORDERING
         int d = i;
         while (isdigit(buffer[d]) && isdigit(s[d]))
            d++;
         if (isdigit(buffer[d]))
            return 1;
         if (isdigit(s.buffer[d]))
            return -1;
#endif
         return toupper(buffer[i]) - toupper(s.buffer[i]);
         }
   return 0;
   }

int String::SlowCompare(const char * s) const
   {
   if (s == NULL)
      return -len;

   for (int i = 0; i <= len; i++)
      if (toupper(buffer[i]) - toupper(s[i]))
         {
#ifdef NATURAL_ORDERING
         int d = i;
         while (isdigit(buffer[d]) && isdigit(s[d]))
            d++;
         if (isdigit(buffer[d]))
            return 1;
         if (isdigit(s[d]))
            return -1;
#endif
         return toupper(buffer[i]) - toupper(s[i]);
         }
   return 0;
   }

String & String::ReadLine(FILE * f)
   {
   len = 0;
   buffer[len] = 0;

   if (f == NULL) return *this;

   int  clen = 0;
   char check[2] = {0, 0};

   int     step = 128;
   String  format("%128[^\n\r]%1[\n\r]");

   while (check[0] != '\n' && check[0] != '\r')
      {
      if (clen)
         {
         step *= 2;
         format.printf("%%%d%s", step, "[^\n\r]%1[\n\r]");
         }
      clen += step;

      int io = fscanf(f, format, LockBuffer(clen) + len, check);
      UnlockBuffer();
      // Avoid getting stuck on zero length lines (system specific!)
      if (io == 0 && check[0] != '\n' && check[0] != '\r')
         fscanf(f, "%1[\n\r]", check);
      if (io == 0 || io == EOF) break;
      }

   if (check[0] == '\n') fscanf(f, "%*1[\r]");
   if (check[0] == '\r') fscanf(f, "%*1[\n]");

   return *this;
   }

String & String::Read(FILE * f)
   {
   len = 0;
   buffer[len] = 0;

   if (f == NULL) return *this;

   int  clen = 0;
   char check[2] = {'G', 0};

   while (strchr(WHITESPACE, check[0]) == NULL)
      {
      clen += READBUF;
      int io = fscanf(f, " %" READBUFSTR "[^" WHITESPACE "]"
                         "%1[" WHITESPACE "]", LockBuffer(clen) + len, check);
      if (io == 0 || io == EOF) break;
      UnlockBuffer();
      }

   return *this;
   }

String & String::Read()
   {
   return Read(stdin);
   }

#ifdef __ZLIB_AVAILABLE__
String & String::Read(IFILE & f)
   {
   len = 0;
   buffer[len] = 0;

   if (f == NULL) return *this;

   bool leading = true;

   while (true)
      {
      int ch = ifgetc(f);

      if (ch == -1) break;

      if (strchr(WHITESPACE, ch) != NULL)
         if (leading)
            continue;
         else
            break;

      if (len + 1 == size)
         Grow(len + 1);

      buffer[len++] = (char) ch;
      buffer[len] = 0;

      leading = false;
      }

   return *this;
   }
#endif

String & String::ReadLine()
   {
   static int last = 0;
   int ch;

   len = 0;
   buffer[len] = 0;

   while (true)
      {
      ch = getchar();

      if (ch == EOF)
         break;

      if (ch == 10)
         if (last == 13)
            { last = 0; continue; }
         else
            { last = 10; break; }

      if (ch == 13)
         if (last == 10)
            { last = 0; continue; }
         else
            { last = 13; break; }

      if (len + 1 == size) Grow(len + 1);

      last = ch;
      buffer[len++] = (char) last;
      buffer[len] = 0;
      }

   return *this;
   }

#ifdef __ZLIB_AVAILABLE__
String & String::ReadLine(IFILE & f)
   {
   static int last = 0;
   int ch;

   len = 0;
   buffer[len] = 0;

   while (true)
      {
      ch = ifgetc(f);

      if (ch == EOF)
         break;

      if (ch == 10)
         if (last == 13)
            { last = 0; continue; }
         else
            { last = 10; break; }

      if (ch == 13)
         if (last == 10)
            { last = 0; continue; }
         else
            { last = 13; break; }

      if (len + 1 == size) Grow(len + 1);

      last = ch;
      buffer[len++] = (char) last;
      buffer[len] = 0;
      }

   return *this;
   }
#endif

void String::Write(FILE * f)
   {
   fprintf(f, "%s", buffer);
   }

void String::Write()
   {
   Write(stdout);
   }

void String::WriteLine()
   {
   WriteLine(stdout);
   }

void String::WriteLine(FILE * f)
   {
   if (f == NULL) return;
   fprintf(f, "%s\n", buffer);
   }

String String::Left(int n) const
   {
   if (n < 0) n = 0;
   if (len < n) n = len;
   String result(n);
   memcpy(result.buffer, buffer, n);
   result.buffer[result.len = n] = 0;
   return result;
   }

String String::Right(int n) const
   {
   if (n < 0) n = 0;
   if (len < n) n = len;
   String result(n);
   memcpy(result.buffer, buffer + len - n, n);
   result.buffer[result.len = n] = 0;
   return result;
   }

String String::SubStr(int start, int n) const
   {
   if (start < 0) { n += start; start = 0; };
   n = min(len - start, n);
   n = max(n, 0);
   String result(n);
   if (start > len) return result;
   memcpy (result.buffer, buffer + start, n);
   result.buffer[result.len = n] = 0;
   return result;
   }

String String::SubStr(int start) const
   {
   return SubStr(start, len - start);
   }

String String::Mid(int start, int end) const
   {
   return SubStr(start, end - start + 1);
   }

int String::FindChar(char ch, int start) const
   {
   return caseSensitive ? FastFindChar(ch, start) : SlowFindChar(ch, start);
   }

int String::FastFindChar(char ch, int start) const
   {
   for ( ; start < len; start++)
      if (buffer[start] == ch)
         return start;
   return -1;
   }

int String::SlowFindChar(char ch, int start) const
   {
   ch = (char) toupper(ch);
   for ( ; start < len; start++)
      if (toupper(buffer[start]) == ch)
         return start;
   return -1;
   }

int String::Find(const String & pattern, int start) const
   {
   return caseSensitive ? FastFind(pattern, start) : SlowFind(pattern, start);
   }

// TODO -- We should have a better string search algorithm

int String::FastFind(const String & pattern, int start) const
   {
   for (int i ; start <= len - pattern.Length(); start++)
      if (buffer[start] == pattern[0])
         {
         for (i = 1; i < pattern.Length(); i++)
            if (pattern[i] != buffer[start + i])
               break;
         if (i == pattern.Length()) return start;
         }
   return -1;
   }

int String::SlowFind(const String & pattern, int start) const
   {
   int firstchar = toupper(pattern[0]);

   for (int i ; start <= len - pattern.Length(); start++)
      if (toupper(buffer[start]) == firstchar)
         {
         for (i = 1; i < pattern.Length(); i++)
            if (toupper(pattern[i]) != toupper(buffer[start + i]))
               break;
         if (i == pattern.Length()) return start;
         }
   return -1;
   }

int String::SetLength(int newlen)
   {
   if (newlen > len)
      {
      Grow(newlen);
      memset(buffer + len, ' ', newlen - len);
      }
   buffer[newlen] = 0;
   return len = newlen;
   }

String & String::Filter(const String & s)
   {
   int to = 0;
   for (int from = 0; from < len; from++)
      if (s.FindChar(buffer[from]) != -1)
          buffer[to++] = buffer[from];
   buffer[len = to] = 0;
   return *this;
   }

String & String::Filter(const char * s)
   {
   String filter(s);
   return Filter(filter);
   }

String & String::ExcludeCharacters(const String & s)
   {
   int to = 0;
   for (int from = 0; from < len; from++)
      if (s.FindChar(buffer[from]) == -1)
          buffer[to++] = buffer[from];
   buffer[len = to] = 0;
   return *this;
   }

String & String::ExcludeCharacters(const char * s)
   {
   String excluded(s);
   return ExcludeCharacters(excluded);
   }

String operator + (const char * lhs, const String & rhs)
   {
   String result(lhs);
   result += rhs;
   return result;
   }

String operator + (char lhs, const String & rhs)
   {
   String result(lhs);
   result += rhs;
   return result;
   }

String operator + (int lhs, const String & rhs)
   {
   String result;
   result = lhs;
   result += rhs;
   return result;
   }

String operator + (unsigned int lhs, const String & rhs)
   {
   String result;
   result = lhs;
   result += rhs;
   return result;
   }

long String::AsInteger() const
   {
   long integer = 0;
   int  base = 10;
   int  pos = 0;
   int  sign = 1;

   if (buffer[pos] == '-')
      sign = -1, pos++;

   if ( len > pos + 2 && buffer[pos] == '0' &&
       (buffer[pos+1] == 'x' || buffer[pos+1] == 'X'))
      base = 16, pos += 2;

   for ( ;  pos < len; pos++)
      {
      char digit = (char) toupper(buffer[pos]);

      if (digit >= '0' && digit <= '9')
         integer = integer * base + digit - '0';
      else if (digit >= 'A' && digit <= 'F' && base == 16)
         integer = integer * base + digit - 'A' + 10;
      else
         return sign * integer;
      }

   return sign * integer;
   }

String & String::Invert()
   {
   for (int i = 0, j = len - 1; i < j; i++, j--)
      {
      char tmp = buffer[i];
      buffer[i] = buffer[j];
      buffer[j] = tmp;
      }
   return *this;
   }

String String::RightToLeft()
   {
   String result(*this);
   result.Invert();
   return result;
   }

String & String::Invert(const String & s)
   {
   Copy(s);
   return Invert();
   }

int String::CompareToStem(const String & stem) const
   {
   if (caseSensitive)
      return String::FastCompareToStem(stem);
   else
      return String::SlowCompareToStem(stem);
   }

int String::FastCompareToStem(const String & stem) const
   {
   for (int i = 0; i < stem.len; i++)
      if (buffer[i] - stem.buffer[i])
         return buffer[i] - stem.buffer[i];
   return 0;
   }

int String::SlowCompareToStem(const String & stem) const
   {
   for (int i = 0; i < stem.len; i++)
      if (toupper(buffer[i]) - toupper(stem.buffer[i]))
         return toupper(buffer[i]) - toupper(stem.buffer[i]);
   return 0;
   }

int String::CompareToStem(const char * stem) const
   {
   if (caseSensitive)
      return String::FastCompareToStem(stem);
   else
      return String::SlowCompareToStem(stem);
   }

int String::FastCompareToStem(const char * stem) const
   {
   for (int i = 0; stem[i] != 0; i++)
      if (buffer[i] - stem[i])
         return buffer[i] - stem[i];
   return 0;
   }

int String::SlowCompareToStem(const char * stem) const
   {
   for (int i = 0; stem[i] != 0; i++)
      if (toupper(buffer[i]) - toupper(stem[i]))
         return toupper(buffer[i]) - toupper(stem[i]);
   return 0;
   }

int String::MatchesBeginningOf(const String & stem) const
   {
   if (caseSensitive)
      return String::FastMatchesBeginningOf(stem);
   else
      return String::SlowMatchesBeginningOf(stem);
   }

int String::FastMatchesBeginningOf(const String & stem) const
   {
   for (int i = 0; i < len; i++)
      if (buffer[i] - stem.buffer[i])
         return buffer[i] - stem.buffer[i];
   return 0;
   }

int String::SlowMatchesBeginningOf(const String & stem) const
   {
   for (int i = 0; i < len; i++)
      if (toupper(buffer[i]) - toupper(stem.buffer[i]))
         return toupper(buffer[i]) - toupper(stem.buffer[i]);
   return 0;
   }

int String::MatchesBeginningOf(const char * stem) const
   {
   if (caseSensitive)
      return String::FastMatchesBeginningOf(stem);
   else
      return String::SlowMatchesBeginningOf(stem);
   }

int String::FastMatchesBeginningOf(const char * stem) const
   {
   for (int i = 0; i < len; i++)
      if (buffer[i] - stem[i])
         return buffer[i] - stem[i];
   return 0;
   }

int String::SlowMatchesBeginningOf(const char * stem) const
   {
   for (int i = 0; i < len; i++)
      if (toupper(buffer[i]) - toupper(stem[i]))
         return toupper(buffer[i]) - toupper(stem[i]);
   return 0;
   }

String & String::Trim(char character)
   {
   int first = 0;
   while (buffer[first] && buffer[first] == character)
      first++;

   int last = len - 1;
   while (last >= 0 && buffer[last] == character)
      last--;

   int out = 0;
   while (first <= last)
      buffer[out++] = buffer[first++];

   buffer[len = out] = 0;

   return *this;
   }

String & String::Trim()
   {
   int first = 0;
   while (buffer[first] && isspace(buffer[first]))
      first++;

   int last = len - 1;
   while (last >= 0 && isspace(buffer[last]))
      last--;

   int out = 0;
   while (first <= last)
      buffer[out++] = buffer[first++];

   buffer[len = out] = 0;

   return *this;
   }

#define VSNPRINTF_NOT_CHECKED    0
#define VSNPRINTF_IS_OK          1
#define VSNPRINTF_NOT_OK         2

int String::vsnprintfChecked = 0;

int String::printf(const char * format, ...)  
   {
   va_list  ap;
   va_start(ap, format);
   
   vprintf(format, ap);
         
   va_end(ap);
   return len;
   }

int String::catprintf(const char * format, ...)
   {
   va_list  ap;
   va_start(ap, format);

   vcatprintf(format, ap);

   va_end(ap);
   return len;
   }

int String::vprintf(const char * format, va_list ap)
   {
   check_vsnprintf();

   while (true)
      {
      int bytes_needed;
      #ifdef va_copy
         va_list arguments;
         va_copy (arguments, ap);
      #else
         va_list & arguments = ap;
      #endif

      if (vsnprintfChecked == VSNPRINTF_IS_OK)
         bytes_needed = vsnprintf(buffer, size, format, arguments);
      else
         bytes_needed = my_vsnprintf(buffer, size, format, arguments);

      #ifdef va_copy
         va_end(arguments);
      #endif

      if (bytes_needed >= size)
         Grow(bytes_needed);
      else if (bytes_needed == -1)
         Grow(size * 2);
      else
         {
         return len = bytes_needed;
         }
      }
   }

void String::check_vsnprintf()
   {
   if (vsnprintfChecked == VSNPRINTF_NOT_CHECKED)
      {
      char temp[100];

      memset(temp, 0, 100);
      int check = snprintf(temp, 5, "%5s", "VSNPRINTF");

      if (temp[6] != 0 || temp[7] != 0 || (check != 9 && check != -1))
         /*
         error("This program requires a working version of vsnprintf\n"
               "However, vsnprintf in the current library seems buggy\n\n"
               "Recompiling this program with the -D__REPLACE_SNPRINTF__ flag\n"
               "may solve this problem.\n\n");
         */
         vsnprintfChecked = VSNPRINTF_NOT_OK;
      else
         vsnprintfChecked = VSNPRINTF_IS_OK;
      }
   }

int String::vcatprintf(const char * format, va_list ap)
   {
   check_vsnprintf();

   if (len == size)
      Grow(size * 2);

   while (true)
      {
      int bytes_needed;
      #ifdef va_copy
         va_list arguments;
         va_copy (arguments, ap);
      #else
         va_list & arguments = ap;
      #endif

      if (vsnprintfChecked == VSNPRINTF_IS_OK)
         bytes_needed = len + vsnprintf(buffer + len, size - len, format, arguments);
      else
         bytes_needed = len + my_vsnprintf(buffer + len, size - len, format, arguments);

      #ifdef va_copy
         va_end(arguments);
      #endif

      if (bytes_needed >= size)
         Grow(bytes_needed);
      else if (bytes_needed < len)
         Grow(size * 2);
      else
         {
         return len = bytes_needed;
         }
      }
   }

FILE * String::my_vsnprintf_file = NULL;

int String::my_vsnprintf(char * buffer, int bufsize, const char * format, va_list args)
   {
   if (my_vsnprintf_file == NULL)
      {
      my_vsnprintf_file = tmpfile();
      atexit(my_vsnprintf_close_file);
      }

   rewind(my_vsnprintf_file);

   int len = vfprintf(my_vsnprintf_file, format, args);

   rewind(my_vsnprintf_file);

   if (len < bufsize)
      buffer[bufsize = len] = 0;
   fread(buffer, 1, bufsize, my_vsnprintf_file);

   return len;
   }

int String::my_snprintf(char * buffer, int bufsize, const char * format, ...)
   {
   va_list  ap;
   va_start(ap, format);

   int bytes = my_vsnprintf(buffer, bufsize, format, ap);

   va_end(ap);

   return bytes;
   }

void String::my_vsnprintf_close_file()
   {
   fclose(my_vsnprintf_file);
   }

bool String::IsNumber()
   {
   int  pos = 0;
   bool digits = false;
    
   // Skip leading sign
   if (buffer[pos] == '-' || buffer[pos] == '+')
      pos++;
   
   // Check integer portion  
   while (buffer[pos] >= '0' && buffer[pos] <= '9')
      pos++, digits = true;
      
   // Skip decimal point
   if (buffer[pos] == '.')
      {
      pos++;
   
      // Check fractional portion
      while (buffer[pos] >= '0' && buffer[pos] <= '9')
         pos++, digits = true;
      }
   
   if (!digits) return false;
         
   // Check exponent
   if (buffer[pos] == 'E' || buffer[pos] == 'e')
      {
      pos++;

      // Skip leading sign
      if (buffer[pos] == '-' || buffer[pos] == '+')
         pos++;
    
      digits = false;

      // Check exponent digits
      while (buffer[pos] >= '0' && buffer[pos] <= '9')
         pos++, digits = true;
      }
   
   return (pos == len) && digits;
   }

void String::Fill(char ch, int length)
   {
   if (length >= 0)
      SetLength(length);

   for (int i = 0; i < len; i++)
      buffer[i] = ch;
   } 
 
